﻿'Jigsaw in VB.Net
'
'A simple jigsaw generator to demonstrate some usefull DrawImage calls
'and how to manipulate bitmap sections from an image
'
'Author: Eugenio Jacobo Hernandez Valdelamar
'2005

'cJigsaw class
'allows to create a new jigsaw (could be a singleton)
'holds as many pieces as the dimension specified require
'look at the DrawImage calls to see how to scale, segment and process an image

Public Class cJigsaw

    'original image
    Private img As Bitmap
    'jigsaw image 
    Private bmpr As Bitmap
    Private g As Graphics

    Public Structure Piece
        Dim id As Integer
        Dim pos As Point
        Dim bmp As Bitmap
    End Structure

    Private parts As Integer
    Private totalCells As Integer
    'array of pieces
    Private pieces() As Piece
    Public pieceSizeW, pieceSizeH As Integer

    'destination picture box
    Private picBack As PictureBox
    '//////////////////////////////////////////////////////////////

    Public Sub New(ByVal pic As PictureBox)
        picBack = pic
        Me.Dimension = 2
    End Sub

    Public Property Dimension()
        Get

        End Get
        Set(ByVal Value)
            parts = Value
            totalCells = parts * parts

            'crear arreglo de piezas
            ReDim pieces(totalCells)
        End Set
    End Property

    'carga imagen de un archivo
    Public Sub loadImage(ByVal fname As String)
        Dim oimg As Bitmap

        'carga imagen de un archivo
        oimg = picBack.Image.FromFile(fname)

        'ajustar tamaño de la imagen al del area de despliege
        img = New Bitmap(picBack.Width, picBack.Height)
        g = Graphics.FromImage(img)
        g.DrawImage(oimg, 0, 0, picBack.Width, picBack.Height)

        'tamaño de las piezas
        pieceSizeW = picBack.Width / parts
        pieceSizeH = picBack.Height / parts
        bmpr = New Bitmap(picBack.Width, picBack.Height)
    End Sub

    'se crea una nueva pieza del rompecabezas
    Private Sub newPiece(ByVal id As Integer, ByVal x As Integer, ByVal y As Integer)
        Dim rect As Rectangle
        Dim xpos, ypos As Integer

        pieces(id) = New Piece
        pieces(id).id = id
        pieces(id).pos.X = x
        pieces(id).pos.Y = y
        pieces(id).bmp = New Bitmap(pieceSizeW, pieceSizeH)
        g = Graphics.FromImage(pieces(id).bmp)
        xpos = x * pieceSizeW
        ypos = y * pieceSizeH
        rect = New Rectangle(xpos, ypos, pieceSizeW - 1, pieceSizeH - 1)
        Dim rDest As New Rectangle(0, 0, pieceSizeW, pieceSizeH)
        g.DrawImage(img, rDest, rect, GraphicsUnit.Pixel)

    End Sub

    'genera las piezas del nuevo rompecabezas
    Public Sub generateJigSaw()
        Dim i, j As Integer
        Dim c As Integer = 1

        For j = 0 To parts - 1
            For i = 0 To parts - 1
                newPiece(c, i, j)
                c += 1
            Next
        Next

        'chkIndices()
    End Sub

    'repinta el rompecabezas
    Public Sub repaintJigSaw()

        Dim i, j As Integer
        Dim r1 As Rectangle

        g = Graphics.FromImage(bmpr)
        g.Clear(Color.Black)
        For i = 1 To totalCells
            r1 = New Rectangle(pieces(i).pos.X * pieceSizeW, pieces(i).pos.Y * pieceSizeH, pieceSizeW - 1, pieceSizeH - 1)
            Dim r2 As New Rectangle(0, 0, pieceSizeW, pieceSizeH)
            g.DrawImage(pieces(i).bmp, r1, r2, GraphicsUnit.Pixel)
            r1 = Nothing
        Next

        picBack.Image = bmpr
    End Sub

    'mezcla las piezas
    Public Sub shuffle()
        Dim i As Integer
        Dim idx1, idx2 As Integer
        Dim rand As New Random
        Dim temp As Piece

        'intercambia las posiciones de las piezas
        For i = 1 To totalCells
            idx1 = rand.Next(1, totalCells)
            idx2 = rand.Next(1, totalCells)
            If idx1 <> idx2 Then
                temp = pieces(idx1)
                pieces(idx1).id = pieces(idx2).id 'asignar indices
                pieces(idx2).id = temp.id

                'rasignar posiciones
                pieces(idx1).pos.X = pieces(idx2).pos.X
                pieces(idx1).pos.Y = pieces(idx2).pos.Y
                pieces(idx2).pos.X = temp.pos.X
                pieces(idx2).pos.Y = temp.pos.Y
            End If
        Next

        'chkIndices()
    End Sub

    'para rastrear el comportamiento de los indices de las piezas
    Private Sub chkIndices()
        Dim i As Integer
        Dim msg As String

        For i = 1 To totalCells
            msg = msg & pieces(i).id & " - "
        Next
        System.Diagnostics.Debug.WriteLine(msg)
    End Sub

    'checa si se logro acomodar la imagen
    Public Function isImgSorted() As Boolean
        Dim i As Integer
        Dim c As Integer = 0

        'chkIndices()
        'revisa si la secuencia (1,2,3,4...) esta presente en
        'el arreglo de piezas
        For i = 1 To totalCells
            If pieces(i).id = i Then
                c += 1
            End If
        Next

        If c = totalCells Then
            isImgSorted = True
        Else
            isImgSorted = False
        End If
    End Function

    'intercambia posiciones de 2 piezas
    Public Sub movePiece2(ByVal xi As Integer, ByVal yi As Integer, ByVal xf As Integer, ByVal yf As Integer)
        Dim t As Integer
        Dim i, j As Integer

        For i = 1 To totalCells
            If pieces(i).pos.X = xi And pieces(i).pos.Y = yi Then
                'intercambia las piezas en las posiciones requeridas
                For j = 1 To totalCells
                    If pieces(j).pos.X = xf And pieces(j).pos.Y = yf Then
                        t = pieces(i).id
                        pieces(i).id = pieces(j).id  'asignar indices
                        pieces(j).id = t
                        pieces(i).pos.X = xf
                        pieces(i).pos.Y = yf
                        pieces(j).pos.X = xi
                        pieces(j).pos.Y = yi
                        repaintJigSaw()
                        Exit Sub
                    End If
                Next
            End If
        Next
    End Sub

    'pinta un hueco en el rompezacezas mientras se decide donde
    'se dejara la pieza seleccionada
    Public Sub putBlank(ByVal x As Integer, ByVal y As Integer)
        Dim rect As Rectangle
        Dim xpos, ypos As Integer

        g = Graphics.FromImage(bmpr)

        xpos = x * pieceSizeW
        ypos = y * pieceSizeH
        rect = New Rectangle(xpos, ypos, pieceSizeW, pieceSizeH)

        g.FillRectangle(Brushes.Green, rect)

        picBack.Image = bmpr
    End Sub
End Class
